﻿using System;
using System.Linq;
using MailKit;
using MimeKit;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;

namespace Wino.Services.Extensions;

public static class MailkitClientExtensions
{
    public static char MailCopyUidSeparator = '_';

    public static uint ResolveUid(string mailCopyId)
    {
        var splitted = mailCopyId.Split(MailCopyUidSeparator);

        if (splitted.Length > 1 && uint.TryParse(splitted[1], out uint parsedUint)) return parsedUint;

        throw new ArgumentOutOfRangeException(nameof(mailCopyId), mailCopyId, "Invalid mailCopyId format.");
    }

    public static UniqueId ResolveUidStruct(string mailCopyId)
        => new UniqueId(ResolveUid(mailCopyId));

    public static string CreateUid(Guid folderId, uint messageUid)
        => $"{folderId}{MailCopyUidSeparator}{messageUid}";

    public static MailImportance GetImportance(this MimeMessage messageSummary)
    {
        if (messageSummary.Headers != null && messageSummary.Headers.Contains(HeaderId.Importance))
        {
            var rawImportance = messageSummary.Headers[HeaderId.Importance];

            return rawImportance switch
            {
                "Low" => MailImportance.Low,
                "High" => MailImportance.High,
                _ => MailImportance.Normal,
            };
        }

        return MailImportance.Normal;
    }

    public static bool GetIsRead(this MessageFlags? flags)
        => flags.GetValueOrDefault().HasFlag(MessageFlags.Seen);

    public static bool GetIsFlagged(this MessageFlags? flags)
        => flags.GetValueOrDefault().HasFlag(MessageFlags.Flagged);

    public static string GetThreadId(this IMessageSummary messageSummary)
    {
        // First check whether we have the default values.

        if (!string.IsNullOrEmpty(messageSummary.ThreadId))
            return messageSummary.ThreadId;

        if (messageSummary.GMailThreadId != null)
            return messageSummary.GMailThreadId.ToString();

        return default;
    }

    public static string GetMessageId(this MimeMessage mimeMessage)
        => mimeMessage.MessageId;

    public static string GetReferences(this MessageIdList messageIdList)
        => string.Join(";", messageIdList);

    public static string GetInReplyTo(this MimeMessage mimeMessage)
    {
        if (mimeMessage.Headers.Contains(HeaderId.InReplyTo))
        {
            // Normalize if <> brackets are there.
            var inReplyTo = mimeMessage.Headers[HeaderId.InReplyTo];

            if (inReplyTo.StartsWith("<") && inReplyTo.EndsWith(">"))
                return inReplyTo.Substring(1, inReplyTo.Length - 2);

            return inReplyTo;
        }

        return string.Empty;
    }

    private static string GetPreviewText(this MimeMessage message)
    {
        if (string.IsNullOrEmpty(message.HtmlBody))
            return message.TextBody;
        else
            return HtmlAgilityPackExtensions.GetPreviewText(message.HtmlBody);
    }

    public static MailCopy GetMailDetails(this IMessageSummary messageSummary, MailItemFolder folder, MimeMessage mime)
    {
        // MessageSummary will only have UniqueId, Flags, ThreadId.
        // Other properties are extracted directly from the MimeMessage.

        // IMAP doesn't have unique id for mails.
        // All mails are mapped to specific folders with incremental Id.
        // Uid 1 may belong to different messages in different folders, but can never be
        // same for different messages in same folders.
        // Here we create arbitrary Id that maps the Id of the message with Folder UniqueId.
        // When folder becomes invalid, we'll clear out these MailCopies as well.

        var messageUid = CreateUid(folder.Id, messageSummary.UniqueId.Id);
        var previewText = mime.GetPreviewText();

        var copy = new MailCopy()
        {
            Id = messageUid,
            CreationDate = mime.Date.UtcDateTime,
            ThreadId = messageSummary.GetThreadId(),
            MessageId = mime.GetMessageId(),
            Subject = mime.Subject,
            IsRead = messageSummary.Flags.GetIsRead(),
            IsFlagged = messageSummary.Flags.GetIsFlagged(),
            PreviewText = previewText,
            FromAddress = GetActualSenderAddress(mime),
            FromName = GetActualSenderName(mime),
            IsFocused = false,
            Importance = mime.GetImportance(),
            References = mime.References?.GetReferences(),
            InReplyTo = mime.GetInReplyTo(),
            HasAttachments = mime.Attachments.Any(),
            FileId = Guid.NewGuid()
        };

        return copy;
    }

    // TODO: Name and Address parsing should be handled better.
    // At some point Wino needs better contact management.

    public static string GetActualSenderName(MimeMessage message)
    {
        if (message == null)
            return string.Empty;

        return message.From.Mailboxes.FirstOrDefault()?.Name ?? message.Sender?.Name ?? Translator.UnknownSender;

        // From MimeKit

        // The "From" header specifies the author(s) of the message.
        // If more than one MimeKit.MailboxAddress is added to the list of "From" addresses,
        // the MimeKit.MimeMessage.Sender should be set to the single MimeKit.MailboxAddress
        // of the personal actually sending the message.

        // Also handle: https://stackoverflow.com/questions/46474030/mailkit-from-address

        //if (message.Sender != null)
        //    return string.IsNullOrEmpty(message.Sender.Name) ? message.Sender.Address : message.Sender.Name;
        //else if (message.From?.Mailboxes != null)
        //{
        //    var firstAvailableName = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Name))?.Name;

        //    if (string.IsNullOrEmpty(firstAvailableName))
        //    {
        //        var firstAvailableAddress = message.From.Mailboxes.FirstOrDefault(a => !string.IsNullOrEmpty(a.Address))?.Address;

        //        if (!string.IsNullOrEmpty(firstAvailableAddress))
        //        {
        //            return firstAvailableAddress;
        //        }
        //    }

        //    return firstAvailableName;
        //}

        //// No sender, no from, I don't know what to do.
        //return Translator.UnknownSender;
    }

    // TODO: This is wrong.
    public static string GetActualSenderAddress(MimeMessage message)
    {
        return message.From.Mailboxes.FirstOrDefault()?.Address ?? message.Sender?.Address ?? Translator.UnknownSender;
        //if (mime == null)
        //    return string.Empty;

        //bool hasSingleFromMailbox = mime.From.Mailboxes.Count() == 1;

        //if (hasSingleFromMailbox)
        //    return mime.From.Mailboxes.First().GetAddress(idnEncode: true);
        //else if (mime.Sender != null)
        //    return mime.Sender.GetAddress(idnEncode: true);
        //else
        //    return Translator.UnknownSender;
    }
}
